Skip to content

Conversation

@groverInnovate
Copy link

Added iOS app by generating iOS Bindings for Circom and Noir separately and then merging them by doing appropriate changes to resolve Binary Symbol Conflicts. Since, both bindings generated generic C-symbols (Rust Buffer and uniffi ..) and identical Runtime Symbols, so thus I used Package Namespacing & Explicit Type Aliasing. Also, I made changes for compatability of backend and database with iOS app and built the final app that uploads to the benchmarks to website with appropriate Metrics

@netlify
Copy link

netlify bot commented Dec 12, 2025

Deploy Preview for deimos-blocsoc ready!

Name Link
🔨 Latest commit f3d147c
🔍 Latest deploy log https://app.netlify.com/projects/deimos-blocsoc/deploys/693c88d1da99ee00089cc7a8
😎 Deploy Preview https://deploy-preview-66--deimos-blocsoc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@groverInnovate groverInnovate mentioned this pull request Dec 12, 2025
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this entire file commented out?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire file is not commented out, It has code for noir and halo2 , rest noir tests and circom and risczero things are commented out . For generating android bindings just uncomment the code of all of these and those bindings would be generated. So, do i need to write a file for this, because i have already written comments in the file .
This is the file -
// use methods::{RISC0_CIRCUIT_ELF, RISC0_CIRCUIT_ID};
// use risc0_zkvm::{default_prover, ExecutorEnv, Receipt};

// Initializes the shared UniFFI scaffolding and defines the MoproError enum.
mopro_ffi::app!();

/// You can also customize the bindings by #[uniffi::export]
/// Reference: https://mozilla.github.io/uniffi-rs/latest/proc_macro/index.html
#[uniffi::export]
fn mopro_uniffi_hello_world() -> String {
"Hello, World!".to_string()
}

#[macro_use]
mod stubs;

mod error;
pub use error::MoproError;

// ==============================================================================
// CIRCOM TEMPLATE (Uncomment to build for Circom)
// ==============================================================================
/*
// Module containing the Circom circuit logic (Multiplier2)
#[macro_use]
mod circom;

rust_witness::witness!(blake2s256);
rust_witness::witness!(keccak);
rust_witness::witness!(mimc256);
rust_witness::witness!(pedersen);
rust_witness::witness!(poseidon);
rust_witness::witness!(sha256);

set_circom_circuits! {
("blake2s256.zkey", circom_prover::witness::WitnessFn::RustWitness(blake2s256_witness)),
("keccak.zkey", circom_prover::witness::WitnessFn::RustWitness(keccak_witness)),
("mimc256.zkey", circom_prover::witness::WitnessFn::RustWitness(mimc256_witness)),
("pedersen.zkey", circom_prover::witness::WitnessFn::RustWitness(pedersen_witness)),
("poseidon.zkey", circom_prover::witness::WitnessFn::RustWitness(poseidon_witness)),
("sha256.zkey", circom_prover::witness::WitnessFn::RustWitness(sha256_witness)),
}
*/

#[cfg(test)]
mod circom_tests {
use crate::circom::{generate_circom_proof, verify_circom_proof, ProofLib};

const ZKEY_PATH: &str = "./test-vectors/circom/mimc256.zkey";

#[test]
fn test_circom() {
    let circuit_inputs = r#"{
"in": [
    "72",
    "101",
    "108",
    "108",
    "111",
    "32",
    "87",
    "111",
    "114",
    "108",
    "100",
    "33",
    "32",
    "84",
    "104",
    "105",
    "115",
    "32",
    "105",
    "115",
    "32",
    "97",
    "32",
    "116",
    "101",
    "115",
    "116",
    "32",
    "109",
    "115",
    "103",
    "46"
]
}"#.to_string();
    let result =
        generate_circom_proof(ZKEY_PATH.to_string(), circuit_inputs, ProofLib::Arkworks);
    assert!(result.is_ok());
    let proof = result.unwrap();
    assert!(verify_circom_proof(ZKEY_PATH.to_string(), proof, ProofLib::Arkworks).is_ok());
}

}

// HALO2_TEMPLATE
// --- Halo2 Example of using Plonk proving and verifying circuits ---

// Module containing the Halo2 circuit logic (FibonacciMoproCircuit)
#[macro_use]
mod halo2;

set_halo2_circuits! {
("plonk_fibonacci_pk.bin", plonk_fibonacci::prove, "plonk_fibonacci_vk.bin", plonk_fibonacci::verify),
("hyperplonk_fibonacci_pk.bin", hyperplonk_fibonacci::prove, "hyperplonk_fibonacci_vk.bin", hyperplonk_fibonacci::verify),
("gemini_fibonacci_pk.bin", gemini_fibonacci::prove, "gemini_fibonacci_vk.bin", gemini_fibonacci::verify),
}

#[cfg(test)]
mod halo2_tests {
use crate::halo2::{generate_halo2_proof, verify_halo2_proof};
use std::collections::HashMap;

#[test]
fn test_plonk_fibonacci() {
    let srs_path = "./test-vectors/halo2/plonk_fibonacci_srs.bin".to_string();
    let pk_path = "./test-vectors/halo2/plonk_fibonacci_pk.bin".to_string();
    let vk_path = "./test-vectors/halo2/plonk_fibonacci_vk.bin".to_string();
    let mut circuit_inputs = HashMap::new();
    circuit_inputs.insert("out".to_string(), vec!["55".to_string()]);
    let result = generate_halo2_proof(srs_path.clone(), pk_path.clone(), circuit_inputs);
    assert!(result.is_ok());
    let halo2_proof_result = result.unwrap();
    let valid = verify_halo2_proof(
        srs_path,
        vk_path,
        halo2_proof_result.proof,
        halo2_proof_result.inputs,
    );
    assert!(valid.is_ok());
    assert!(valid.unwrap());
}

#[test]
fn test_hyperplonk_fibonacci() {
    let srs_path = "./test-vectors/halo2/hyperplonk_fibonacci_srs.bin".to_string();
    let pk_path = "./test-vectors/halo2/hyperplonk_fibonacci_pk.bin".to_string();
    let vk_path = "./test-vectors/halo2/hyperplonk_fibonacci_vk.bin".to_string();
    let mut circuit_inputs = HashMap::new();
    circuit_inputs.insert("out".to_string(), vec!["55".to_string()]);
    let result = generate_halo2_proof(srs_path.clone(), pk_path.clone(), circuit_inputs);
    assert!(result.is_ok());
    let halo2_proof_result = result.unwrap();
    let valid = verify_halo2_proof(
        srs_path,
        vk_path,
        halo2_proof_result.proof,
        halo2_proof_result.inputs,
    );
    assert!(valid.is_ok());
    assert!(valid.unwrap());
}

#[test]
fn test_gemini_fibonacci() {
    let srs_path = "./test-vectors/halo2/gemini_fibonacci_srs.bin".to_string();
    let pk_path = "./test-vectors/halo2/gemini_fibonacci_pk.bin".to_string();
    let vk_path = "./test-vectors/halo2/gemini_fibonacci_vk.bin".to_string();
    let mut circuit_inputs = HashMap::new();
    circuit_inputs.insert("out".to_string(), vec!["55".to_string()]);
    let result = generate_halo2_proof(srs_path.clone(), pk_path.clone(), circuit_inputs);
    assert!(result.is_ok());
    let halo2_proof_result = result.unwrap();
    let valid = verify_halo2_proof(
        srs_path,
        vk_path,
        halo2_proof_result.proof,
        halo2_proof_result.inputs,
    );
    assert!(valid.is_ok());
    assert!(valid.unwrap());
}

}

// NOIR_TEMPLATE
// --- Noir Example of using Ultra Honk proving and verifying circuits ---

// Module containing the Noir circuit logic (Multiplier2)
mod noir;
#[cfg(test)]
// mod noir_tests {
// use super::noir::{generate_noir_proof, get_noir_verification_key, verify_noir_proof};
// use serial_test::serial;

// #[test]
// #[serial]
// fn test_noir_multiplier2() {
// let srs_path = "./test-vectors/noir/noir_multiplier2.srs".to_string();
// let circuit_path = "./test-vectors/noir/noir_multiplier2.json".to_string();
// let circuit_inputs = vec!["3".to_string(), "5".to_string()];
// let vk = get_noir_verification_key(
// circuit_path.clone(),
// Some(srs_path.clone()),
// true, // on_chain (uses Keccak for Solidity compatibility)
// false, // low_memory_mode
// )
// .unwrap();

// let proof = generate_noir_proof(
// circuit_path.clone(),
// Some(srs_path.clone()),
// circuit_inputs.clone(),
// true, // on_chain (uses Keccak for Solidity compatibility)
// vk.clone(),
// false, // low_memory_mode
// )
// .unwrap();

// let valid = verify_noir_proof(
// circuit_path,
// proof,
// true, // on_chain (uses Keccak for Solidity compatibility)
// vk,
// false, // low_memory_mode
// )
// .unwrap();
// assert!(valid);
// }

// #[test]
// #[serial]
// fn test_noir_multiplier2_with_existing_vk() {
// let srs_path = "./test-vectors/noir/noir_multiplier2.srs".to_string();
// let circuit_path = "./test-vectors/noir/noir_multiplier2.json".to_string();
// let vk_path = "./test-vectors/noir/noir_multiplier2.vk".to_string();

// // read vk from file as Vec
// let vk = std::fs::read(vk_path).unwrap();

// let circuit_inputs = vec!["3".to_string(), "5".to_string()];

// let proof = generate_noir_proof(
// circuit_path.clone(),
// Some(srs_path),
// circuit_inputs,
// true, // on_chain (uses Keccak for Solidity compatibility)
// vk.clone(),
// false, // low_memory_mode
// )
// .unwrap();

// let valid = verify_noir_proof(
// circuit_path,
// proof,
// true, // on_chain (uses Keccak for Solidity compatibility)
// vk,
// false, // low_memory_mode
// )
// .unwrap();
// assert!(valid);
// .unwrap();
// assert!(valid);
// }
// }

#[cfg(test)]
mod uniffi_tests {
#[test]
fn test_mopro_uniffi_hello_world() {
assert_eq!(super::mopro_uniffi_hello_world(), "Hello, World!");
}
}

// #[derive(uniffi::Error, thiserror::Error, Debug)]
// pub enum Risc0Error {
// #[error("Failed to prove: {0}")]
// ProveError(String),
// #[error("Failed to serialize receipt: {0}")]
// SerializeError(String),
// #[error("Failed to verify: {0}")]
// VerifyError(String),
// #[error("Failed to decode journal: {0}")]
// DecodeError(String),
// }

// #[derive(uniffi::Record, Clone)]
// pub struct Risc0ProofOutput {
// pub receipt: Vec,
// }

// #[derive(uniffi::Record, Clone)]
// pub struct Risc0VerifyOutput {
// pub is_valid: bool,
// pub output_value: u32,
// }

// #[uniffi::export]
// pub fn risc0_prove(input: u32) -> Result<Risc0ProofOutput, Risc0Error> {
// // Create executor environment with input
// let env = ExecutorEnv::builder()
// .write(&input)
// .map_err(|e| Risc0Error::ProveError(format!("Failed to write input: {}", e)))?
// .build()
// .map_err(|e| {
// Risc0Error::ProveError(format!("Failed to build executor environment: {}", e))
// })?;

// // Get the default prover
// let prover = default_prover();

// // Generate proof
// let prove_info = prover
// .prove(env, RISC0_CIRCUIT_ELF)
// .map_err(|e| Risc0Error::ProveError(format!("Failed to generate proof: {}", e)))?;

// // Extract receipt
// let receipt = prove_info.receipt;

// // Serialize receipt to bytes
// let receipt_bytes = bincode::serialize(&receipt)
// .map_err(|e| Risc0Error::SerializeError(format!("Failed to serialize receipt: {}", e)))?;

// Ok(Risc0ProofOutput {
// receipt: receipt_bytes,
// })
// }

// #[uniffi::export]
// pub fn risc0_verify(receipt_bytes: Vec) -> Result<Risc0VerifyOutput, Risc0Error> {
// // Deserialize receipt from bytes
// let receipt: Receipt = bincode::deserialize(&receipt_bytes)
// .map_err(|e| Risc0Error::SerializeError(format!("Failed to deserialize receipt: {}", e)))?;

// // Verify the receipt
// receipt
// .verify(RISC0_CIRCUIT_ID)
// .map_err(|e| Risc0Error::VerifyError(format!("Failed to verify receipt: {}", e)))?;

// // Extract output from journal
// let output_value: u32 = receipt
// .journal
// .decode()
// .map_err(|e| Risc0Error::DecodeError(format!("Failed to decode journal: {}", e)))?;

// Ok(Risc0VerifyOutput {
// is_valid: true,
// output_value,
// })
// }

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_risc0_prove_success() {
    // Test proving with a simple u32 input
    let input = 42u32;
    let result = risc0_prove(input);

    assert!(result.is_ok(), "Proving should succeed for valid input");

    let proof_output = result.unwrap();
    assert!(
        !proof_output.receipt.is_empty(),
        "Receipt should not be empty"
    );
}

#[test]
fn test_risc0_verify_success() {
    // First generate a proof
    let input = 123u32;
    let prove_result = risc0_prove(input);
    assert!(prove_result.is_ok(), "Proving should succeed");

    let proof_output = prove_result.unwrap();

    // Now verify the proof
    let verify_result = risc0_verify(proof_output.receipt);
    assert!(
        verify_result.is_ok(),
        "Verification should succeed for valid proof"
    );

    let verify_output = verify_result.unwrap();
    assert!(verify_output.is_valid, "Proof should be valid");
    assert_eq!(
        verify_output.output_value, input,
        "Output value should match input"
    );
}

#[test]
fn test_prove_verify_roundtrip() {
    // Test the complete prove -> verify workflow with multiple inputs
    let test_inputs = [0u32, 42u32, 100u32, 1000u32, u32::MAX];

    for &input in &test_inputs {
        // Generate proof
        let prove_result = risc0_prove(input);
        assert!(
            prove_result.is_ok(),
            "Proving should succeed for input: {}",
            input
        );

        let proof_output = prove_result.unwrap();

        // Verify proof
        let verify_result = risc0_verify(proof_output.receipt);
        assert!(
            verify_result.is_ok(),
            "Verification should succeed for input: {}",
            input
        );

        let verify_output = verify_result.unwrap();
        assert!(
            verify_output.is_valid,
            "Proof should be valid for input: {}",
            input
        );
        assert_eq!(
            verify_output.output_value, input,
            "Output should match input: {}",
            input
        );
    }
}

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noir tests are also commented out. If you have generated bindings for both circom and noir and combined them and have made necessary changes then revert lib.rs and other files the way before or divert out the whole ios flag because if this is how ios development is proceeded it will be tedious work every time and for building android we have to un comment and redo things, let aside the merging problems

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can uncomment the test, but they don't play a role in Bindings generation. The lib.rs is entirely the same except for comments, why is that a problem? I don't understand, how is it tedious to uncomment few lines of code. Why just uncessarily increase the size of our project. Is there any other change other than commenting out lines of lib.rs that cause a problem in bindings generation of android?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats the use of these scripts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These scripts are iOS-specific hacks.
When we build for iOS, both Circom and Noir generate Swift code that uses generic names like RustBuffer and RustCallStatus. If we include both, the iOS compiler yells "Duplicate symbol" and crashes.
These scripts open the generated Swift files and rename those types to CircomRustBuffer and NoirRustBuffer. This allows both frameworks to exist side-by-side in the same iOS app.

@AnInsaneJimJam
Copy link

what are these file changes 💀💀

@groverInnovate
Copy link
Author

what are these file changes 💀💀

What happened?

@AnInsaneJimJam
Copy link

How are you building circom bindings for ios when the lib.rs circom part is commented out?

@groverInnovate
Copy link
Author

How are you building circom bindings for ios when the lib.rs circom part is commented out?

Just comment the noir and halo2 part and uncomment the circom part, you have to do it in 2 parts. You also need to make changes in Cargo.toml and Config.toml to choose adapters for circom and noir and how to do those changes, I've written the comments for iOS and for android everything is just same, just uncomment everything.

@AnInsaneJimJam
Copy link

Make a script for ios which comments and build and then uncomments the files. This doesn't look scalable and looks hacky solution.

@netlify
Copy link

netlify bot commented Dec 30, 2025

Deploy Preview for deimos-blocsoc canceled.

Name Link
🔨 Latest commit 1378d47
🔍 Latest deploy log https://app.netlify.com/projects/deimos-blocsoc/deploys/69580adac27b3b000842a9ce

@x-senpai-x x-senpai-x merged commit 6f93c58 into BlocSoc-iitr:dev Jan 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants